using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

namespace ReactiveCode.Linq
{
    using Util;

    public static class EnumerableObserver
    {
        class AnonymousEnumerableObserver<T> : IEnumerableObserver<T>
        {
            internal Action<IEnumerable<IObservable<T>>> _onReset;
            internal Action<IObservable<T>, int> _onAdded, _onRemoved;
            internal Action<Exception> _onError;
            internal Action _onCompleted;

            [ContractInvariantMethod]
            void ObjectContract()
            {
                Contract.Invariant(_onAdded != null && _onRemoved != null && _onReset != null && _onError != null);
            }

            public void OnReset(IEnumerable<IObservable<T>> value)
            {
                _onReset(value);
            }

            public void OnAdded(IObservable<T> item, int index)
            {
                _onAdded(item, index);
            }

            public void OnRemoved(IObservable<T> item, int index)
            {
                _onRemoved(item, index);
            }

            public void OnCompleted()
            {
                if (_onCompleted != null)
                    _onCompleted();
            }

            public void OnError(Exception error)
            {
                _onError(error);
            }
        }
        public static IEnumerableObserver<T> Create<T>(Action<IObservable<T>, int> onAdded, Action<IObservable<T>, int> onRemoved, Action<IEnumerable<IObservable<T>>> onReset, Action<Exception> onError, Action onCompleted = null)
        {
            Contract.Requires(onAdded != null && onRemoved != null && onReset != null && onError != null);
            return new AnonymousEnumerableObserver<T>
            {
                _onReset = onReset,
                _onAdded = onAdded,
                _onRemoved = onRemoved,
                _onError = onError,
                _onCompleted = onCompleted
            };
        }
        public static IEnumerableObserver<T> Create<T>(Action<IObservable<T>, int> onAdded, Action<IObservable<T>, int> onRemoved, Action<IEnumerable<IObservable<T>>> onReset)
        {
            return Create<T>(onAdded, onRemoved, onReset, ex => { throw ex; });
        }

        internal static void OnCleared<T>(this IEnumerableObserver<T> observer)
        {
            Contract.Requires(observer != null);
            observer.OnReset(Enumerable.Empty<IObservable<T>>());
        }
        internal static void OnAdded<T>(this IEnumerableObserver<T> observer, IObservable<T> item)
        {
            Contract.Requires(observer != null && item != null);
            observer.OnAdded(item, -1);
        }
        internal static void OnRemoved<T>(this IEnumerableObserver<T> observer, IObservable<T> item)
        {
            Contract.Requires(observer != null && item != null);
            observer.OnRemoved(item, -1);
        }
    }
}
